package com.pdfscan;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.common.GlobalHistogramBinarizer;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;

public class PDFBarcodeScannerGUI extends JFrame {
    private static final Logger logger = LoggerFactory.getLogger(PDFBarcodeScannerGUI.class);
    private static final String OUTPUT_DIRECTORY = "output";
    
    private JTextField pdfFilePathField;
    private JTextArea logTextArea;
    private JButton browseButton;
    private JButton processButton;
    private JProgressBar progressBar;
    
    public PDFBarcodeScannerGUI() {
        setTitle("PDF条形码扫描与分类工具");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        initComponents();
        layoutComponents();
        
        // 创建输出目录
        File outputDir = new File(OUTPUT_DIRECTORY);
        if (!outputDir.exists()) {
            outputDir.mkdir();
        }
    }
    
    private void initComponents() {
        pdfFilePathField = new JTextField(30);
        pdfFilePathField.setEditable(false);
        
        browseButton = new JButton("选择PDF文件");
        browseButton.addActionListener(this::browseButtonClicked);
        
        processButton = new JButton("处理文件");
        processButton.addActionListener(this::processButtonClicked);
        processButton.setEnabled(false);
        
        logTextArea = new JTextArea(20, 60);
        logTextArea.setEditable(false);
        
        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
    }
    
    private void layoutComponents() {
        JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
        mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
        
        // 文件选择面板
        JPanel filePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        filePanel.add(new JLabel("PDF文件:"));
        filePanel.add(pdfFilePathField);
        filePanel.add(browseButton);
        filePanel.add(processButton);
        
        // 日志面板
        JScrollPane logScrollPane = new JScrollPane(logTextArea);
        logScrollPane.setBorder(BorderFactory.createTitledBorder("处理日志"));
        
        // 进度条面板
        JPanel progressPanel = new JPanel(new BorderLayout());
        progressPanel.add(progressBar, BorderLayout.CENTER);
        
        // 添加到主面板
        mainPanel.add(filePanel, BorderLayout.NORTH);
        mainPanel.add(logScrollPane, BorderLayout.CENTER);
        mainPanel.add(progressPanel, BorderLayout.SOUTH);
        
        setContentPane(mainPanel);
    }
    
    private void browseButtonClicked(ActionEvent e) {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(new FileNameExtensionFilter("PDF文件", "pdf"));
        
        int result = fileChooser.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            pdfFilePathField.setText(selectedFile.getAbsolutePath());
            processButton.setEnabled(true);
        }
    }
    
    private void processButtonClicked(ActionEvent e) {
        String pdfFilePath = pdfFilePathField.getText().trim();
        if (pdfFilePath.isEmpty()) {
            JOptionPane.showMessageDialog(this, "请选择PDF文件", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        // 禁用按钮，防止重复处理
        browseButton.setEnabled(false);
        processButton.setEnabled(false);
        progressBar.setValue(0);
        logTextArea.setText("");
        
        // 在后台线程中处理文件
        SwingWorker<Void, String> worker = new SwingWorker<>() {
            @Override
            protected Void doInBackground() {
                try {
                    File pdfFile = new File(pdfFilePath);
                    scanAndClassifyPdf(pdfFile);
                } catch (Exception ex) {
                    publish("错误: " + ex.getMessage());
                    logger.error("处理PDF文件时发生错误", ex);
                }
                return null;
            }
            
            @Override
            protected void process(List<String> chunks) {
                for (String chunk : chunks) {
                    logTextArea.append(chunk + "\n");
                    // 自动滚动到底部
                    logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
                }
            }
            
            @Override
            protected void done() {
                browseButton.setEnabled(true);
                processButton.setEnabled(true);
                progressBar.setValue(100);
                publish("处理完成。输出文件保存在 " + OUTPUT_DIRECTORY + " 目录下。");
            }
        };
        
        worker.execute();
    }
    
    private void appendLog(String message) {
        logTextArea.append(message + "\n");
        // 自动滚动到底部
        logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
    }
    
    private void scanAndClassifyPdf(File pdfFile) throws IOException {
        try (PDDocument document = PDDocument.load(pdfFile)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            int pageCount = document.getNumberOfPages();
            
            if (pageCount == 0) {
                appendLog("警告: PDF文件不包含任何页面");
                return;
            }
            
            appendLog("开始处理PDF文件: " + pdfFile.getName() + ", 共 " + pageCount + " 页");
            
            // 扫描所有页面的条形码
            List<String> barcodes = new ArrayList<>();
            for (int i = 0; i < pageCount; i++) {
                final int currentPage = i;
                // 更新进度条
                SwingUtilities.invokeLater(() -> {
                    progressBar.setValue((currentPage * 50) / pageCount);
                });
                
                BufferedImage image = pdfRenderer.renderImageWithDPI(i, 400);
                String barcode = scanBarcodeFromImage(image);
                barcodes.add(barcode);
                appendLog("页面 " + (i + 1) + ": 条形码 " + (barcode != null ? barcode : "未检测到"));
            }
            
            // 按条形码分组页面
            appendLog("根据条形码对页面进行分组...");
            Map<String, List<Integer>> barcodeGroups = classifyPagesByBarcode(barcodes);
            
            // 创建分组后的PDF文件
            appendLog("创建分类后的PDF文件...");
            createClassifiedPdfFiles(document, barcodeGroups, pdfFile.getName());
            
        } catch (IOException e) {
            appendLog("错误: 无法读取PDF文件");
            logger.error("无法读取PDF文件", e);
            throw e;
        }
    }

    private String scanBarcodeFromImage(BufferedImage image) {
        try {
            Map<DecodeHintType, Object> hints = new HashMap<>();
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
//                    BarcodeFormat.CODE_39,
//                    BarcodeFormat.CODE_93,
//                    BarcodeFormat.CODE_128,
//                    BarcodeFormat.EAN_8,
//                    BarcodeFormat.EAN_13,
                    BarcodeFormat.QR_CODE
            ));
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");

            // 优先用HybridBinarizer
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            try {
                Result result = new MultiFormatReader().decode(bitmap, hints);
                if (result != null && !"**".equals(result.getText())) {
                    return result.getText();
                }
            } catch (NotFoundException ignore) {}

            // 再用GlobalHistogramBinarizer尝试一次
            bitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));
            try {
                Result result = new MultiFormatReader().decode(bitmap, hints);
                if (result != null && !"**".equals(result.getText())) {
                    return result.getText();
                }
            } catch (NotFoundException ignore) {}

            // 识别不到返回null
            return null;
        } catch (Exception e) {
            logger.error("扫描条形码时发生错误", e);
            return null;
        }
    }

    private Map<String, List<Integer>> classifyPagesByBarcode(List<String> barcodes) {
        Map<String, List<Integer>> groups = new HashMap<>();
        
        if (barcodes.isEmpty()) {
            return groups;
        }

        String currentBarcode = null;
        
        for (int i = 0; i < barcodes.size(); i++) {
            String barcode = barcodes.get(i);
            
            // 如果页面有条形码，设置为当前处理条码
            if (barcode != null && !barcode.isEmpty()) {
//                groups.computeIfAbsent("无条形码", k -> new ArrayList<>()).add(i);
                currentBarcode = barcode;
            }
            groups.computeIfAbsent(currentBarcode, k -> new ArrayList<>()).add(i);
        }
        
        return groups;
    }

    private void createClassifiedPdfFiles(PDDocument originalDocument, 
                                         Map<String, List<Integer>> barcodeGroups,
                                         String originalFileName) throws IOException {
        String baseFileName = originalFileName.replaceAll("\\.pdf$", "");
        int groupCount = barcodeGroups.size();
        int currentGroup = 0;
        
        for (Map.Entry<String, List<Integer>> entry : barcodeGroups.entrySet()) {
            String barcode = entry.getKey();
            List<Integer> pageIndexes = entry.getValue();
            currentGroup++;
            
            // 更新进度条
            final int progress = 50 + (currentGroup * 50) / groupCount;
            SwingUtilities.invokeLater(() -> progressBar.setValue(progress));
            
            if (pageIndexes.isEmpty()) {
                continue;
            }
            
            try (PDDocument newDocument = new PDDocument()) {
                // 复制页面到新文档
                for (int pageIndex : pageIndexes) {
                    PDPage page = originalDocument.getPage(pageIndex);
                    newDocument.addPage(page);
                }
                
                // 保存新文档
                String safeBarcode = barcode.replaceAll("[\\\\/:*?\"<>|]", "_"); // 替换不安全的文件名字符
                String outputFileName = String.format("%s/%s_%s.pdf", 
                        OUTPUT_DIRECTORY, baseFileName, safeBarcode);
                newDocument.save(outputFileName);
                appendLog("已创建分类文件: " + outputFileName + ", 包含 " + pageIndexes.size() + " 页");
            }
        }
    }
    
    public static void main(String[] args) {
        // 在EDT中启动GUI
        SwingUtilities.invokeLater(() -> {
            PDFBarcodeScannerGUI gui = new PDFBarcodeScannerGUI();
            gui.setVisible(true);
        });
    }
} 